package edu.byu.intex2.data.dao;

import edu.byu.intex2.data.Cache;
import edu.byu.intex2.data.ConnectionPool;
import edu.byu.intex2.data.DataException;
import edu.byu.intex2.data.bo.NewCar;
import edu.byu.intex2.data.bo.Person;
import java.sql.*;
import java.util.List;

/**
 * NewCar Data Access Object
 * @author Brady White
 */
public class NewCarDAO {

    /////////////////////////////////////////////
    ///   Singleton code
    private static NewCarDAO instance = null;

    /** Creates a new instance of NewCarDAO */
    private NewCarDAO() {
    }//constructor

    /** Retrieves the single instance of this class */
    public static synchronized NewCarDAO getInstance() {
        if (instance == null) {
            instance = new NewCarDAO();
        }
        return instance;
    }//getInstance

    ////////////////////////////////////////////
    ///   CREATE methods

    /** Creates a new NewCar */
    public NewCar create(String id) throws DataException {
        // Create new NewCar with Guid
        NewCar newCar = new NewCar(id);
        newCar.setObjectAlreadyInDB(false); // set alreadyInDB to false

        // put NewCar in cache
        Cache.getInstance().put(newCar.getId(), newCar);
        return newCar;
    }//create


    ////////////////////////////////////////////
    ///   READ methods
    
    /** Reads an existing NewCar from the database   */
    public NewCar read(String id) throws DataException {
        // Check Cache
        if (Cache.getInstance().containsKey(id)) {
            return (NewCar) Cache.getInstance().get(id);
        }

        // Get Connection
        Connection conn = ConnectionPool.getInstance().get();

        // Try Reading with connection
        try {
            return read(id, conn);
        } catch (Exception e) {
            throw new DataException("An error occurred while reading the business object information.", e);
        } finally {
            ConnectionPool.getInstance().release(conn);
        }
    }

    /** Internal method to read an existing NewCar from the database    */
    synchronized NewCar read(String id, Connection conn) throws Exception {
        // Check Cache
        if (Cache.getInstance().containsKey(id)) {
            return (NewCar) Cache.getInstance().get(id);
        }

        // Prepare a SQL Statement
        PreparedStatement stmt = conn.prepareStatement("SELECT * FROM new_car where id=?");

        try {
            stmt.setString(1, id);
            ResultSet rs = stmt.executeQuery(); // execute query
            if (rs.next()) {
                // Read the record with the result set
                return readRecord(rs, conn);
            }//if
            throw new DataException("NewCar with id '" + id + "' not found.");
        } finally {
            stmt.close();
        }
    }//read

    /**
     * Internal method to create a NewCar object from a record
     * @return newCar
     */
    synchronized NewCar readRecord(ResultSet rs, Connection conn) throws Exception {
        // Check cache for object
        if (Cache.getInstance().containsKey(rs.getString("id"))) {
            return (NewCar) Cache.getInstance().get(rs.getString("id"));
        }

        // Instantiate new newCar
        NewCar newCar = new NewCar(rs.getString("id"));

        // Object is already in database
        newCar.setObjectAlreadyInDB(true);

        // Set each attribute to object
        newCar.setMSRP(rs.getDouble("msrp"));

        // Set Person values
        PhysicalCarDAO.getInstance().setPhysicalCarValues(newCar, conn);

        // Object is not dirty
        newCar.setDirty(false);

        // Put object in cache
        Cache.getInstance().put(newCar.getId(), newCar);

        return newCar;
    }//readRecord

    /////////////////////////////////////////////
    ///   UPDATE methods
    /** Saves an existing newCar in the database */
    public void save(NewCar newCar) throws DataException {
        // get a connection from the pool, save all of the sales first using the other newCar method and then commit them in the database
        Connection conn = ConnectionPool.getInstance().get();

        try {
            save(newCar, conn); // save with connection
            conn.commit();  // commit the db transaction
        } catch (Exception e) {
            try {
                //rollback if there were any errors saving, catch any other errors you might have
                conn.rollback();
            } catch (SQLException e2) {
                throw new DataException("Could not roll back the newCar database transaction!", e2);
            }
            throw new DataException("An error occurred while saving the newCar object information.", e);
        } finally {
            // release the connection
            ConnectionPool.getInstance().release(conn);
        }
    }

    /** Internal method to update a newCar in the database */
    void save(NewCar newCar, Connection conn) throws Exception {
        // put the newCar object to cache
        Cache.getInstance().put(newCar.getId(), newCar);

        // decide whether to update or insert into the database
        if (newCar.isObjectAlreadyInDB() && newCar.isDirty()) {
            // Update PhysicalCar
            PhysicalCarDAO.getInstance().saveNoCommit(newCar, conn);

            // Update NewCar
            update(newCar, conn);
        } else if (newCar.isDirty()) {
            // Insert PhysicalCar
            PhysicalCarDAO.getInstance().saveNoCommit(newCar, conn);

            // Insert NewCar
            insert(newCar, conn);
        }//if
    }

    /** Saves an existing newCar to the database */
    private void update(NewCar newCar, Connection conn) throws Exception {
        // use a prepared statement to prevent sql injection
        PreparedStatement stmt = conn.prepareStatement("UPDATE new_car SET msrp=?  WHERE id=?");
        try {
            // Get Person Attributes and add them to the statement
            stmt.setDouble(1, newCar.getMSRP());
            stmt.setString(2, newCar.getId());
            stmt.execute();

            // Person is clean!
            newCar.setDirty(false);
        } finally {
            stmt.close();  // make sure to close the prepared statement
        }
    }

    /** Inserts a new newCar into the database */
    private void insert(NewCar newCar, Connection conn) throws Exception {
        PreparedStatement stmt = conn.prepareStatement("INSERT INTO new_car (id, msrp) VALUES (?, ?)");
        try {
            // Set NewCar Attributes
            stmt.setString(1, newCar.getId());
            stmt.setDouble(2, newCar.getMSRP());
            stmt.execute();

            newCar.setObjectAlreadyInDB(true); // now in DB
            newCar.setDirty(false); // object is clean
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            stmt.close(); // close the prepared statement
        }
    }

    /////////////////////////////////////////////////
    ///   DELETE methods
    /** We do not support deleting of business objects in this application */
    public void delete(NewCar newCar) throws DataException {
        throw new RuntimeException("Nice try. The delete function is not supported in this application.");
    }


    ////////////////////////////////////////////////
    ///   SEARCH methods


}
